diff options
Diffstat (limited to 'app/[lng]/admin/ecc/page.tsx')
| -rw-r--r-- | app/[lng]/admin/ecc/page.tsx | 474 |
1 files changed, 365 insertions, 109 deletions
diff --git a/app/[lng]/admin/ecc/page.tsx b/app/[lng]/admin/ecc/page.tsx index 257ecfea..cd09e213 100644 --- a/app/[lng]/admin/ecc/page.tsx +++ b/app/[lng]/admin/ecc/page.tsx @@ -10,7 +10,7 @@ import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { Alert, AlertDescription } from '@/components/ui/alert' -import { Loader2, Play, CheckCircle, XCircle, Send } from 'lucide-react' +import { Loader2, Play, CheckCircle, XCircle, Send, Plus, Minus } from 'lucide-react' import { toast } from 'sonner' // SOAP 송신 함수들 import @@ -125,36 +125,96 @@ export default function ECCSenderTestPage() { // PO 생성 테스트 const [poData, setPoData] = useState({ - // 헤더 정보 - ANFNR: 'TEST001', - LIFNR: '1000000001', - ZPROC_IND: 'A', - ANGNR: 'TEST001', - WAERS: 'KRW', - ZTERM: '0001', - INCO1: 'FOB', - INCO2: 'Seoul, Korea', - MWSKZ: 'V0', - LANDS: 'KR', - ZRCV_DT: '20241201', - ZATTEN_IND: 'Y', - IHRAN: '20241201', - TEXT: 'Test PO Creation', - // 아이템 정보 - ANFPS: '00001', - NETPR: '1000.00', - PEINH: '1', - BPRME: 'EA', - NETWR: '1000.00', - BRTWR: '1100.00', - LFDAT: '20241201', - // PR 반환 정보 - EBELN: 'PR001', - EBELP: '00001', - MSGTY: 'S', - MSGTXT: 'Test message' + header: { + ANFNR: 'TEST001', + LIFNR: '1000000001', + ZPROC_IND: 'A', + ANGNR: 'TEST001', + WAERS: 'KRW', + ZTERM: '0001', + INCO1: 'FOB', + INCO2: 'Seoul, Korea', + MWSKZ: 'V0', + LANDS: 'KR', + ZRCV_DT: '20241201', + ZATTEN_IND: 'Y', + IHRAN: '20241201', + TEXT: 'Test PO Creation', + LSTEL: '', + VSTEL: '', + ZDLV_CNTLR: '', + ZDLV_PRICE_NOTE: '', + ZDLV_PRICE_T: '' + }, + items: [{ + ANFNR: 'TEST001', + ANFPS: '00001', + LIFNR: '1000000001', + NETPR: '1000.00', + PEINH: '1', + BPRME: 'EA', + NETWR: '1000.00', + BRTWR: '1100.00', + LFDAT: '20241201', + EBELP: '', + ZCON_NO_PO: '' + }], + prReturn: [{ + ANFNR: 'TEST001', + ANFPS: '00001', + EBELN: 'PR001', + EBELP: '00001', + MSGTY: 'S', + MSGTXT: 'Test message' + }] }) + // PO 아이템 관리 함수들 + const addPoItem = () => { + const newItemIndex = poData.items.length + 1 + setPoData(prev => ({ + ...prev, + items: [...prev.items, { + ANFNR: prev.header.ANFNR, + ANFPS: String(newItemIndex).padStart(5, '0'), + LIFNR: prev.header.LIFNR, + NETPR: '0.00', + PEINH: '1', + BPRME: 'EA', + NETWR: '0.00', + BRTWR: '0.00', + LFDAT: prev.header.IHRAN, + EBELP: '', + ZCON_NO_PO: '' + }] + })) + } + + const removePoItem = (index: number) => { + if (poData.items.length > 1) { + setPoData(prev => ({ + ...prev, + items: prev.items.filter((_, i) => i !== index) + })) + } + } + + const updatePoItem = (index: number, field: string, value: string) => { + setPoData(prev => ({ + ...prev, + items: prev.items.map((item, i) => + i === index ? { ...item, [field]: value } : item + ) + })) + } + + const updatePoHeader = (field: string, value: string) => { + setPoData(prev => ({ + ...prev, + header: { ...prev.header, [field]: value } + })) + } + return ( <div className="container mx-auto py-8 space-y-6"> <div className="flex items-center justify-between"> @@ -513,87 +573,188 @@ export default function ECCSenderTestPage() { PO (Purchase Order) 생성 </CardTitle> <CardDescription> - 구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE) + 구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE) - 하나의 PO에 여러 PR 아이템들을 포함시키는 구조 </CardDescription> </CardHeader> - <CardContent className="space-y-4"> + <CardContent className="space-y-6"> + {/* PO 헤더 정보 */} <div className="space-y-4"> - <h4 className="font-semibold">PO 헤더 정보</h4> - <div className="grid grid-cols-2 gap-4"> + <div className="flex items-center justify-between"> + <h4 className="font-semibold">PO 헤더 정보</h4> + <Badge variant="outline" className="text-xs"> + {poData.items.length}개 아이템 + </Badge> + </div> + <div className="grid grid-cols-3 gap-4"> <div className="space-y-2"> - <Label htmlFor="po-anfnr">입찰번호 (필수)</Label> + <Label htmlFor="po-anfnr">입찰번호 (ANFNR) <span className="text-red-500">*</span></Label> <Input id="po-anfnr" - value={poData.ANFNR} - onChange={(e) => setPoData(prev => ({ ...prev, ANFNR: e.target.value }))} - placeholder="입찰번호" + value={poData.header.ANFNR} + onChange={(e) => updatePoHeader('ANFNR', e.target.value)} + placeholder="입찰번호 (ANFNR)" /> </div> <div className="space-y-2"> - <Label htmlFor="po-lifnr">공급업체 계정 (필수)</Label> + <Label htmlFor="po-lifnr">공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label> <Input id="po-lifnr" - value={poData.LIFNR} - onChange={(e) => setPoData(prev => ({ ...prev, LIFNR: e.target.value }))} - placeholder="공급업체 계정번호" + value={poData.header.LIFNR} + onChange={(e) => updatePoHeader('LIFNR', e.target.value)} + placeholder="공급업체 계정 (LIFNR) : 벤더코드" /> </div> <div className="space-y-2"> - <Label htmlFor="po-zproc">처리상태 (필수)</Label> + <Label htmlFor="po-zproc">처리상태 (ZPROC_IND) <span className="text-red-500">*</span></Label> <Input id="po-zproc" - value={poData.ZPROC_IND} - onChange={(e) => setPoData(prev => ({ ...prev, ZPROC_IND: e.target.value }))} - placeholder="구매처리상태" + value={poData.header.ZPROC_IND} + onChange={(e) => updatePoHeader('ZPROC_IND', e.target.value)} + placeholder="처리상태 (ZPROC_IND)" /> </div> <div className="space-y-2"> - <Label htmlFor="po-waers">통화 (필수)</Label> + <Label htmlFor="po-angnr">협상번호 (ANGNR)</Label> + <Input + id="po-angnr" + value={poData.header.ANGNR} + onChange={(e) => updatePoHeader('ANGNR', e.target.value)} + placeholder="협상번호 (ANGNR)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-waers">통화 (WAERS) <span className="text-red-500">*</span></Label> <Input id="po-waers" - value={poData.WAERS} - onChange={(e) => setPoData(prev => ({ ...prev, WAERS: e.target.value }))} - placeholder="통화 코드" + value={poData.header.WAERS} + onChange={(e) => updatePoHeader('WAERS', e.target.value)} + placeholder="통화 (WAERS)" /> </div> - </div> - - <h4 className="font-semibold">PO 아이템 정보</h4> - <div className="grid grid-cols-2 gap-4"> <div className="space-y-2"> - <Label htmlFor="po-anfps">입찰 아이템번호 (필수)</Label> + <Label htmlFor="po-zterm">지급조건 (ZTERM) <span className="text-red-500">*</span></Label> <Input - id="po-anfps" - value={poData.ANFPS} - onChange={(e) => setPoData(prev => ({ ...prev, ANFPS: e.target.value }))} - placeholder="입찰 아이템번호" + id="po-zterm" + value={poData.header.ZTERM} + onChange={(e) => updatePoHeader('ZTERM', e.target.value)} + placeholder="지급조건 (ZTERM)" /> </div> <div className="space-y-2"> - <Label htmlFor="po-netpr">순가격 (필수)</Label> + <Label htmlFor="po-inco1">인코텀즈1 (INCO1) <span className="text-red-500">*</span></Label> <Input - id="po-netpr" - value={poData.NETPR} - onChange={(e) => setPoData(prev => ({ ...prev, NETPR: e.target.value }))} - placeholder="순가격" + id="po-inco1" + value={poData.header.INCO1} + onChange={(e) => updatePoHeader('INCO1', e.target.value)} + placeholder="인코텀즈1 (INCO1)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-inco2">인코텀즈2 (INCO2) <span className="text-red-500">*</span></Label> + <Input + id="po-inco2" + value={poData.header.INCO2} + onChange={(e) => updatePoHeader('INCO2', e.target.value)} + placeholder="인코텀즈2 (INCO2)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-mwskz">세금코드 (MWSKZ) <span className="text-red-500">*</span></Label> + <Input + id="po-mwskz" + value={poData.header.MWSKZ} + onChange={(e) => updatePoHeader('MWSKZ', e.target.value)} + placeholder="세금코드 (MWSKZ)" /> </div> <div className="space-y-2"> - <Label htmlFor="po-bprme">주문단위 (필수)</Label> + <Label htmlFor="po-lands">국가키 (LANDS) <span className="text-red-500">*</span></Label> <Input - id="po-bprme" - value={poData.BPRME} - onChange={(e) => setPoData(prev => ({ ...prev, BPRME: e.target.value }))} - placeholder="주문단위 (예: EA)" + id="po-lands" + value={poData.header.LANDS} + onChange={(e) => updatePoHeader('LANDS', e.target.value)} + placeholder="국가키 (LANDS)" /> </div> <div className="space-y-2"> - <Label htmlFor="po-lfdat">납기일 (필수)</Label> + <Label htmlFor="po-zrcv-dt">수령일 (ZRCV_DT) <span className="text-red-500">*</span></Label> <Input - id="po-lfdat" - value={poData.LFDAT} - onChange={(e) => setPoData(prev => ({ ...prev, LFDAT: e.target.value }))} - placeholder="YYYYMMDD 형식" + id="po-zrcv-dt" + value={poData.header.ZRCV_DT} + onChange={(e) => updatePoHeader('ZRCV_DT', e.target.value)} + placeholder="수령일 (ZRCV_DT)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-zatten-ind">참석지시자 (ZATTEN_IND) <span className="text-red-500">*</span></Label> + <Input + id="po-zatten-ind" + value={poData.header.ZATTEN_IND} + onChange={(e) => updatePoHeader('ZATTEN_IND', e.target.value)} + placeholder="참석지시자 (ZATTEN_IND)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-ihran">입찰마감일 (IHRAN) <span className="text-red-500">*</span></Label> + <Input + id="po-ihran" + value={poData.header.IHRAN} + onChange={(e) => updatePoHeader('IHRAN', e.target.value)} + placeholder="입찰마감일 (IHRAN)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-text">텍스트 (TEXT)</Label> + <Input + id="po-text" + value={poData.header.TEXT} + onChange={(e) => updatePoHeader('TEXT', e.target.value)} + placeholder="텍스트 (TEXT)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-lstel">배송지점 (LSTEL)</Label> + <Input + id="po-lstel" + value={poData.header.LSTEL} + onChange={(e) => updatePoHeader('LSTEL', e.target.value)} + placeholder="배송지점 (LSTEL)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-vstel">출하지점 (VSTEL)</Label> + <Input + id="po-vstel" + value={poData.header.VSTEL} + onChange={(e) => updatePoHeader('VSTEL', e.target.value)} + placeholder="출하지점 (VSTEL)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-zdlv-cntl">납품담당자 (ZDLV_CNTLR)</Label> + <Input + id="po-zdlv-cntl" + value={poData.header.ZDLV_CNTLR} + onChange={(e) => updatePoHeader('ZDLV_CNTLR', e.target.value)} + placeholder="납품담당자 (ZDLV_CNTLR)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-zdlv-price-note">가격노트 (ZDLV_PRICE_NOTE)</Label> + <Input + id="po-zdlv-price-note" + value={poData.header.ZDLV_PRICE_NOTE} + onChange={(e) => updatePoHeader('ZDLV_PRICE_NOTE', e.target.value)} + placeholder="가격노트 (ZDLV_PRICE_NOTE)" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="po-zdlv-price-t">가격유형 (ZDLV_PRICE_T)</Label> + <Input + id="po-zdlv-price-t" + value={poData.header.ZDLV_PRICE_T} + onChange={(e) => updatePoHeader('ZDLV_PRICE_T', e.target.value)} + placeholder="가격유형 (ZDLV_PRICE_T)" /> </div> </div> @@ -601,6 +762,133 @@ export default function ECCSenderTestPage() { <Separator /> + {/* PO 아이템 정보 */} + <div className="space-y-4"> + <div className="flex items-center justify-between"> + <h4 className="font-semibold">PO 아이템 정보</h4> + <Button + onClick={addPoItem} + size="sm" + variant="outline" + className="flex items-center gap-1" + > + <Plus className="h-4 w-4" /> + 아이템 추가 + </Button> + </div> + + {poData.items.map((item, index) => ( + <Card key={index} className="p-4"> + <div className="flex items-center justify-between mb-4"> + <h5 className="font-medium">아이템 #{index + 1}</h5> + {poData.items.length > 1 && ( + <Button + onClick={() => removePoItem(index)} + size="sm" + variant="destructive" + className="flex items-center gap-1" + > + <Minus className="h-4 w-4" /> + 삭제 + </Button> + )} + </div> + <div className="grid grid-cols-3 gap-4"> + <div className="space-y-2"> + <Label>입찰번호 (ANFNR) <span className="text-red-500">*</span></Label> + <Input + value={item.ANFNR} + onChange={(e) => updatePoItem(index, 'ANFNR', e.target.value)} + placeholder="입찰번호 (ANFNR)" + /> + </div> + <div className="space-y-2"> + <Label>입찰 아이템번호 (ANFPS) <span className="text-red-500">*</span></Label> + <Input + value={item.ANFPS} + onChange={(e) => updatePoItem(index, 'ANFPS', e.target.value)} + placeholder="입찰 아이템번호 (ANFPS)" + /> + </div> + <div className="space-y-2"> + <Label>공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label> + <Input + value={item.LIFNR} + onChange={(e) => updatePoItem(index, 'LIFNR', e.target.value)} + placeholder="공급업체 계정 (LIFNR) : 벤더코드" + /> + </div> + <div className="space-y-2"> + <Label>순가격 (NETPR) <span className="text-red-500">*</span></Label> + <Input + value={item.NETPR} + onChange={(e) => updatePoItem(index, 'NETPR', e.target.value)} + placeholder="순가격 (NETPR)" + /> + </div> + <div className="space-y-2"> + <Label>가격단위 (PEINH) <span className="text-red-500">*</span></Label> + <Input + value={item.PEINH} + onChange={(e) => updatePoItem(index, 'PEINH', e.target.value)} + placeholder="가격단위 (PEINH)" + /> + </div> + <div className="space-y-2"> + <Label>주문단위 (BPRME) <span className="text-red-500">*</span></Label> + <Input + value={item.BPRME} + onChange={(e) => updatePoItem(index, 'BPRME', e.target.value)} + placeholder="주문단위 (BPRME)" + /> + </div> + <div className="space-y-2"> + <Label>순금액 (NETWR) <span className="text-red-500">*</span></Label> + <Input + value={item.NETWR} + onChange={(e) => updatePoItem(index, 'NETWR', e.target.value)} + placeholder="순금액 (NETWR)" + /> + </div> + <div className="space-y-2"> + <Label>총금액 (BRTWR) <span className="text-red-500">*</span></Label> + <Input + value={item.BRTWR} + onChange={(e) => updatePoItem(index, 'BRTWR', e.target.value)} + placeholder="총금액 (BRTWR)" + /> + </div> + <div className="space-y-2"> + <Label>납기일 (LFDAT) <span className="text-red-500">*</span></Label> + <Input + value={item.LFDAT} + onChange={(e) => updatePoItem(index, 'LFDAT', e.target.value)} + placeholder="납기일 (LFDAT)" + /> + </div> + <div className="space-y-2"> + <Label>구매오더 품번 (EBELP)</Label> + <Input + value={item.EBELP} + onChange={(e) => updatePoItem(index, 'EBELP', e.target.value)} + placeholder="구매오더 품번 (EBELP)" + /> + </div> + <div className="space-y-2"> + <Label>계약번호 (ZCON_NO_PO)</Label> + <Input + value={item.ZCON_NO_PO} + onChange={(e) => updatePoItem(index, 'ZCON_NO_PO', e.target.value)} + placeholder="계약번호 (ZCON_NO_PO)" + /> + </div> + </div> + </Card> + ))} + </div> + + <Separator /> + <div className="flex gap-4"> <Button onClick={() => runTest('PO 생성 (샘플)', () => createTestPurchaseOrder())} @@ -614,41 +902,9 @@ export default function ECCSenderTestPage() { <Button onClick={() => runTest('PO 생성 (사용자)', async () => { const poRequest = { - T_Bidding_HEADER: [{ - ANFNR: poData.ANFNR, - LIFNR: poData.LIFNR, - ZPROC_IND: poData.ZPROC_IND, - ANGNR: poData.ANGNR, - WAERS: poData.WAERS, - ZTERM: poData.ZTERM, - INCO1: poData.INCO1, - INCO2: poData.INCO2, - MWSKZ: poData.MWSKZ, - LANDS: poData.LANDS, - ZRCV_DT: poData.ZRCV_DT, - ZATTEN_IND: poData.ZATTEN_IND, - IHRAN: poData.IHRAN, - TEXT: poData.TEXT - }], - T_Bidding_ITEM: [{ - ANFNR: poData.ANFNR, - ANFPS: poData.ANFPS, - LIFNR: poData.LIFNR, - NETPR: poData.NETPR, - PEINH: poData.PEINH, - BPRME: poData.BPRME, - NETWR: poData.NETWR, - BRTWR: poData.BRTWR, - LFDAT: poData.LFDAT - }], - T_PR_RETURN: [{ - ANFNR: poData.ANFNR, - ANFPS: poData.ANFPS, - EBELN: poData.EBELN, - EBELP: poData.EBELP, - MSGTY: poData.MSGTY, - MSGTXT: poData.MSGTXT - }] + T_Bidding_HEADER: [poData.header], + T_Bidding_ITEM: poData.items, + T_PR_RETURN: poData.prReturn } return createPurchaseOrder(poRequest) })} @@ -656,7 +912,7 @@ export default function ECCSenderTestPage() { > {isLoading['PO 생성 (사용자)'] && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} <Send className="mr-2 h-4 w-4" /> - 사용자 데이터로 생성 + 사용자 데이터로 생성 ({poData.items.length}개 아이템) </Button> </div> |
